#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <assert.h>

#include <string>
#include <vector>
#include <fstream>

#include "gui/gui.h"

#include "../scintilla/ILexer.h"
#include "../scintilla/Scintilla.h"
#include "../scintilla/SplitVector.h"
#include "../scintilla/Partitioning.h"
#include "../scintilla/RunStyles.h"
#include "../scintilla/CellBuffer.h"
#include "../scintilla/CharacterSet.h"
#include "../scintilla/Decoration.h"
#include "../scintilla/CharClassify.h"
#include "../scintilla/Document.h"
#include "../scintilla/PerLine.h"
#include "../scintilla/RESearch.h"
#include "../scintilla/UniConversion.h"

#include "../scintilla/SciLexer.h"
#include "../scintilla/PropSetSimple.h"
#include "../scintilla/WordList.h"
#include "../scintilla/LexAccessor.h"
#include "../scintilla/Accessor.h"
#include "../scintilla/StyleContext.h"
#include "../scintilla/LexerModule.h"
#include "../scintilla/LexerBase.h"
#include "../scintilla/LexerSimple.h"
#include "../scintilla/Catalogue.h"


class LexState : public LexInterface {
    const LexerModule *lexCurrent;
    void SetLexerModule(const LexerModule *lex);
    PropSetSimple props;
public:
    int lexLanguage;

    LexState(Document *pdoc_);
    virtual ~LexState();
    void SetLexer(uptr_t wParam);
    void SetLexerLanguage(const char *languageName);
    const char *DescribeWordListSets();
    void SetWordList(int n, const char *wl);
    int GetStyleBitsNeeded() const;
    const char *GetName() const;
    void *PrivateCall(int operation, void *pointer);
    const char *PropertyNames();
    int PropertyType(const char *name);
    const char *DescribeProperty(const char *name);
    void PropSet(const char *key, const char *val);
    const char *PropGet(const char *key) const;
    int PropGetInt(const char *key, int defaultValue=0) const;
    int PropGetExpanded(const char *key, char *result) const;
};


LexState::LexState(Document *pdoc_) : LexInterface(pdoc_) {
    lexCurrent = 0;
    performingStyle = false;
    lexLanguage = SCLEX_CONTAINER;
}

LexState::~LexState() {
    if (instance) {
        instance->Release();
        instance = 0;
    }
}


void LexState::SetLexerModule(const LexerModule *lex) {
    if (lex != lexCurrent) {
        if (instance) {
            instance->Release();
            instance = 0;
        }
        lexCurrent = lex;
        if (lexCurrent)
            instance = lexCurrent->Create();
        pdoc->LexerChanged();
    }
}

void LexState::SetLexer(uptr_t wParam) {
//    lexLanguage = wParam;
//     if (lexLanguage == SCLEX_CONTAINER) {
//         SetLexerModule(0);
//     } else {
//         const LexerModule *lex = Catalogue::Find(lexLanguage);
//         if (!lex)
//             lex = Catalogue::Find(SCLEX_NULL);
//         SetLexerModule(lex);
//     }
    SetLexerLanguage((const char *)wParam);
}

void LexState::SetLexerLanguage(const char *languageName) {
    const LexerModule *lex = Catalogue::Find(languageName);
    if (!lex)
        lex = Catalogue::Find(SCLEX_NULL);
    if (lex)
        lexLanguage = lex->GetLanguage();
    SetLexerModule(lex);
}

const char *LexState::DescribeWordListSets() {
    if (instance) {
        return instance->DescribeWordListSets();
    } else {
        return 0;
    }
}

void LexState::SetWordList(int n, const char *wl) {
    if (instance) {
        int firstModification = instance->WordListSet(n, wl);
        if (firstModification >= 0) {
            pdoc->ModifiedAt(firstModification);
        }
    }
}

int LexState::GetStyleBitsNeeded() const {
    return lexCurrent ? lexCurrent->GetStyleBitsNeeded() : 5;
}

const char *LexState::GetName() const {
    return lexCurrent ? lexCurrent->languageName : "";
}

void *LexState::PrivateCall(int operation, void *pointer) {
    if (pdoc && instance) {
        return instance->PrivateCall(operation, pointer);
    } else {
        return 0;
    }
}

const char *LexState::PropertyNames() {
    if (instance) {
        return instance->PropertyNames();
    } else {
        return 0;
    }
}

int LexState::PropertyType(const char *name) {
    if (instance) {
        return instance->PropertyType(name);
    } else {
        return SC_TYPE_BOOLEAN;
    }
}

const char *LexState::DescribeProperty(const char *name) {
    if (instance) {
        return instance->DescribeProperty(name);
    } else {
        return 0;
    }
}

void LexState::PropSet(const char *key, const char *val) {
    props.Set(key, val);
    if (instance) {
        int firstModification = instance->PropertySet(key, val);
        if (firstModification >= 0) {
            pdoc->ModifiedAt(firstModification);
        }
    }
}

const char *LexState::PropGet(const char *key) const {
    return props.Get(key);
}

int LexState::PropGetInt(const char *key, int defaultValue) const {
    return props.GetInt(key, defaultValue);
}

int LexState::PropGetExpanded(const char *key, char *result) const {
    return props.GetExpanded(key, result);
}

void test_lex()
{
    using namespace std;
    Scintilla_LinkLexers();

    ifstream fin("TestView.cpp", ios::in);
    if (!fin.is_open()) return;
    Document doc;
    const int MAX_LEN = 4096;
    char buffer[MAX_LEN];
    int len = 0;
    while (fin.read(buffer, MAX_LEN), len = fin.gcount(), len != 0)
        doc.InsertString(0, buffer, len);
    fin.close();
    len = doc.Length();
    doc.GetCharRange(buffer, 0, 10);
    buffer[10] = 0;
    LexState li(&doc);
    li.SetLexerLanguage("cpp");
    TEST_FUNC(doc.EnsureStyledTo(len - 1));
}